/*
 *    Copyright (C) 2011-2012 Hai Bison
 *
 *    See the file LICENSE at the root directory of this project for copying
 *    permission.
 */

package group.pals.mailgate.preferences;

import group.pals.ciphers.Crypto;
import group.pals.ciphers.SecureCipher;
import group.pals.mailgate.beans.ItemEmailAccount;
import group.pals.mailgate.beans.ItemEmailProvider;
import group.pals.mailgate.utils.Utils;
import group.pals.utils.net.Proxy;
import group.pals.utils.sys.MessageBox;
import group.pals.utils.text.StrUtils;
import group.pals.utils.xml.Parser;
import java.awt.SystemTray;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringEscapeUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 * @author Hai Bison
 */
public class Preferences {

    /*----------------------------------------------------------------------------
     * KEYS AND CONSTANTS
     */
    public static final String KeyRoot = "mail-gate-preferences";
    public static final String KeySecurity = "security";
    public static final String KeyAuthentication = "authentication";
    public static final String KeyMasterPassword = "master-password";
    public static final String KeyAuthUsername = "auth-username";
    public static final String KeyRandomPassword = "random-password";
    public static final String KeyConnection = "connection";
    public static final String KeyEmailCheckTimePeriod = "mail-check-time-period";
    public static final int DefEmailCheckTimePeriod = (int) (60e3 * 5);
    public static final String KeyConnectionTimeout = "timeout";
    public static final int DefConnectionTimeout = (int) (60e3 / 2);
    public static final String KeyUseSOCKSProxy = "use-socks-proxy";
    public static final String KeySOCKSProxyHost = "socks-proxy-host";
    public static final String KeySOCKSProxyPort = "socks-proxy-port";
    public static final String KeySOCKSProxyUsername = "socks-proxy-username";
    public static final String KeySOCKSProxyPassword = "socks-proxy-password";
    public static final String KeyEmailAccounts = "e-mail-accounts";
    public static final String KeyItem = "item";
    public static final String KeyEmailAddress = "e-mail-address";
    public static final String KeyEmailPassword = "password";
    public static final String KeyEmailProviders = "e-mail-providers";
//    public static final String KeyItem = "item";
    public static final String KeyEmailPattern = "e-mail-pattern";
    public static final String KeyImapHost = "imap-host";
    public static final String KeyImapPort = "imap-port";
    public static final int DefImapPort = 993;
    public static final String KeyView = "view";
    public static final String KeyMinimizeOnStart = "minimize-on-start";
    public static final String KeyGroupEmailsByConversation = "group-emails-by-conversation";
    public static final String KeyPopupWindow = "popup-window";
    public static final String KeyPopupWindowAnimation = "animation";
    public static final boolean DefPopupWindowAnimation = false;
    public static final String KeyPopupWindowDelayTime = "delay-time";
    public static final int DefPopupWindowDelayTime = (int) 30e3;
    public static final String KeyPopupWindowWidth = "width";
    public static final int DefPopupWindowWidth = 400;
    public static final String KeyPopupWindowHeight = "height";
    public static final int DefPopupWindowHeight = 300;
    public static final String KeyPopupWindowPaddingWidth = "padding-width";
    public static final int DefPopupWindowPaddingWidth = 80;
    public static final String KeyPopupWindowPosition = "position";
    public static final int DefPopupWindowPosition = 4;
    public static final String KeyMouseDblClickSensitivity = "mouse-dbl-click-sensitivity";
    public static final int DefMouseDblClickSensitivity = 300;
    public static final String KeyNotification = "notification";
    public static final String KeyPlaySound = "play-sound";
    public static final boolean DefPlaySound = false;
    public static final String KeySoundFile = "sound-file";
    public static final String KeyUserActions = "user-actions";
    public static final String KeyLastWorkingDir = "last-working-dir";
    public static final String KeyAppPreferences = "app-preferences";
    public static final String KeyAutoUpdate = "auto-update";
    public static final boolean DefAutoUpdate = true;

    /*----------------------------------------------------------------------------
     * ATTRIBUTES
     */
    public static final String AttrName = "name";
    public static final String AttrEnabled = "enabled";
    private final Utils _Utils;
    private final File PreferencesFile;
    private final Parser XmlParser;
    private final SecureCipher InternalCipher;
    private final List<PreferencesListener> Listeners;

    private Preferences() {
        _Utils = Utils.getInstance();
        PreferencesFile = new File(
                String.format("%s/%s", _Utils.getStartupDir().getAbsolutePath(), Utils.PreferencesFilename));
        if (PreferencesFile.isFile()) {
            XmlParser = new Parser(false, null, null);
            try {
                XmlParser.load(PreferencesFile);
            } catch (Exception ex) {
                MessageBox.showErrMsg(null, null,
                        String.format("Error on loading preferences: %s\n"
                        + "The application will create new file to replace the corruptted one.",
                        ex));
            }
        } else {
            XmlParser = new Parser(true, KeyRoot, null);
        }

        this.InternalCipher = new SecureCipher("");
        this.Listeners = new ArrayList<PreferencesListener>();
    }

    public void store() {
        try {
            XmlParser.store(PreferencesFile);
            for (PreferencesListener listener : Listeners) {
                listener.preferencesStored();
            }
        } catch (Exception ex) {
            MessageBox.showErrMsg(null, null,
                    String.format("Error on storing preferences: %s\n."
                    + "Create new file to replace the corruptted one.",
                    ex));
        }
    }

    public void addPreferencesListener(PreferencesListener listener) {
        if (!Listeners.contains(listener)) {
            Listeners.add(listener);
        }
    }

    public void removePreferencesListener(PreferencesListener listener) {
        Listeners.remove(listener);
    }

    /*----------------------------------------------------------------------------
     * Security
     */
    /**
     * Test user login.
     * @return <code>true</code> if login passed.
     */
    public boolean userLogin(String password) {
        InternalCipher.setNew(password);
        if (getMasterPassword() == null) {
            return false;
        }
        return getMasterPassword().equals(password);
    }

    public boolean isUseMasterPassword() {
        Element el = XmlParser.getElement(null, new String[]{KeySecurity, KeyAuthentication, KeyMasterPassword}, false);
        if (el == null) {
            return false;
        } else {
            return StrUtils.StrTrue.equals(el.getAttribute(AttrEnabled));
        }
    }

    public void setUseMasterPassword(boolean value) {
        Element el = XmlParser.getElement(null, new String[]{KeySecurity, KeyAuthentication, KeyMasterPassword}, true);
        el.setAttribute(AttrEnabled, value ? StrUtils.StrTrue : StrUtils.StrFalse);
    }

    public String getMasterPassword() {
        Element el = XmlParser.getElement(null, new String[]{KeySecurity, KeyAuthentication, KeyMasterPassword}, false);
        if (el == null || el.getTextContent().trim().isEmpty()) {
            //set default master password to an empty string
            el = XmlParser.getElement(null, new String[]{KeySecurity, KeyAuthentication, KeyMasterPassword}, true);
            el.setTextContent(StringEscapeUtils.escapeXml(Crypto.encryptStr_256("", InternalCipher.getCipher())));
        }
        String password = Crypto.decryptStr_256(
                StringEscapeUtils.unescapeXml(el.getTextContent().trim()), InternalCipher.getCipher());
        return password;
    }

    public void setMasterPassword(String value) {
        String randomPassword = getRandomPassword();

        InternalCipher.setNew(value);
        Element el = XmlParser.getElement(null, new String[]{KeySecurity, KeyAuthentication, KeyMasterPassword}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(
                Crypto.encryptStr_256(InternalCipher.getCipher(), InternalCipher.getCipher())));

        setRandomPassword(randomPassword);
    }

    public String getAuthUsername() {
        Element el = XmlParser.getElement(null, new String[]{KeySecurity, KeyAuthentication, KeyAuthUsername}, false);
        if (el == null) {
            return null;
        }
        String username = Crypto.decryptStr_256(StringEscapeUtils.unescapeXml(el.getTextContent()), Utils.JvmUniqueID);
        if (username != null) {
            username = username.replaceFirst(String.format("^%s", Pattern.quote(Utils.JvmUniqueID)), "");
        }
        return username;
    }

    public void setAuthUsername(String value) {
        Element el = XmlParser.getElement(null, new String[]{KeySecurity, KeyAuthentication, KeyAuthUsername}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(Crypto.encryptStr_256(Utils.JvmUniqueID + value, Utils.JvmUniqueID)));
    }

    private String getRandomPassword() {
        Random random = new Random();
        Element el = XmlParser.getElement(null, new String[]{KeySecurity, KeyAuthentication, KeyRandomPassword}, false);
        String password = null;
        if (el == null || el.getTextContent().trim().isEmpty()) {
            password = "";
            for (int i = 0; i < Byte.MAX_VALUE; i++) {
                password += (char) (random.nextInt(Byte.MAX_VALUE));
            }//for
            setRandomPassword(password);
        } else {
            password = Crypto.decryptStr_256(
                    StringEscapeUtils.unescapeXml(el.getTextContent().trim()), getMasterPassword());
        }

        return password;
    }

    private void setRandomPassword(String value) {
        Element el = XmlParser.getElement(null, new String[]{KeySecurity, KeyAuthentication, KeyRandomPassword}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(
                Crypto.encryptStr_256(value, getMasterPassword())));
    }

    /*----------------------------------------------------------------------------
     * Connection
     */
    public int getEmailCheckTimePeriod() {
        Element el = XmlParser.getElement(null, new String[]{KeyConnection, KeyEmailCheckTimePeriod}, true);
        try {
            return new Integer(StringEscapeUtils.unescapeXml(el.getTextContent()));
        } catch (Exception ex) {
            return DefEmailCheckTimePeriod;
        }
    }

    public void setEmailCheckTimePeriod(int value) {
        Element el = XmlParser.getElement(null, new String[]{KeyConnection, KeyEmailCheckTimePeriod}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(Integer.toString(value)));
    }

    public int getConnectionTimeout() {
        Element el = XmlParser.getElement(null, new String[]{KeyConnection, KeyConnectionTimeout}, true);
        try {
            return new Integer(StringEscapeUtils.unescapeXml(el.getTextContent()));
        } catch (Exception ex) {
            return DefConnectionTimeout;
        }
    }

    public void setConnectionTimeout(int value) {
        Element el = XmlParser.getElement(null, new String[]{KeyConnection, KeyConnectionTimeout}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(Integer.toString(value)));
    }

    public boolean isUseSOCKSProxy() {
        Element el = XmlParser.getElement(null, new String[]{KeyConnection, KeyUseSOCKSProxy}, true);
        return StrUtils.StrTrue.equals(el.getAttribute(AttrEnabled));
    }

    public void setUseSOCKSProxy(boolean value) {
        Element el = XmlParser.getElement(null, new String[]{KeyConnection, KeyUseSOCKSProxy}, true);
        el.setAttribute(AttrEnabled, value ? StrUtils.StrTrue : StrUtils.StrFalse);
    }

    public void setSOCKSProxy(Proxy proxy) {
        Element el = XmlParser.getElement(null, new String[]{KeyConnection, KeyUseSOCKSProxy}, true);
        XmlParser.removeAllChildNodes(el);
        Document document = XmlParser.getDocument();

        Element item = document.createElement(KeySOCKSProxyHost);
        item.setTextContent(StringEscapeUtils.escapeXml(proxy.getAddress()));
        el.appendChild(item);

        item = document.createElement(KeySOCKSProxyPort);
        item.setTextContent(Integer.toString(proxy.getPort()));
        el.appendChild(item);

        item = document.createElement(KeySOCKSProxyUsername);
        item.setTextContent(StringEscapeUtils.escapeXml(
                Crypto.encryptStr_256(proxy.getUsername(), getRandomPassword())));
        el.appendChild(item);

        item = document.createElement(KeySOCKSProxyPassword);
        item.setTextContent(StringEscapeUtils.escapeXml(
                Crypto.encryptStr_256(proxy.getPassword(), getRandomPassword())));
        el.appendChild(item);
    }

    public Proxy getSOCKSProxy() {
        Proxy result = new Proxy(null, 0);

        Element el = XmlParser.getElement(null, new String[]{KeyConnection, KeyUseSOCKSProxy}, true);
        NodeList childNodes = el.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node item = childNodes.item(i);
            if (item.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }

            if (item.getNodeName().equals(KeySOCKSProxyHost)) {
                result.setAddress(StringEscapeUtils.unescapeXml(item.getTextContent()));
            } else if (item.getNodeName().equals(KeySOCKSProxyPort)) {
                try {
                    result.setPort(new Integer(StringEscapeUtils.unescapeXml(item.getTextContent())));
                } catch (NumberFormatException nfe) {
                }
            } else if (item.getNodeName().equals(KeySOCKSProxyUsername)) {
                result.setUsername(Crypto.decryptStr_256(
                        StringEscapeUtils.unescapeXml(item.getTextContent()), getRandomPassword()));
            } else if (item.getNodeName().equals(KeySOCKSProxyPassword)) {
                result.setUsername(Crypto.decryptStr_256(
                        StringEscapeUtils.unescapeXml(item.getTextContent()), getRandomPassword()));
            }
        }

        return result;
    }

    /*----------------------------------------------------------------------------
     * View
     */
    public boolean isMinimizeOnStart() {
        if (!SystemTray.isSupported()) {
            return false;
        }

        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyMinimizeOnStart}, false);
        if (el == null) {
            setMinimizeOnStart(false);
            return false;
        } else {
            return StrUtils.StrTrue.equals(el.getTextContent());
        }
    }

    public void setMinimizeOnStart(boolean value) {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyMinimizeOnStart}, true);
        el.setTextContent(value ? StrUtils.StrTrue : StrUtils.StrFalse);
    }

    public boolean isGroupEmailsByConversation() {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyGroupEmailsByConversation}, false);
        if (el == null) {
            setGroupEmailsByConversation(true);
            return true;
        } else {
            return StrUtils.StrTrue.equals(el.getTextContent());
        }
    }

    public void setGroupEmailsByConversation(boolean value) {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyGroupEmailsByConversation}, true);
        el.setTextContent(value ? StrUtils.StrTrue : StrUtils.StrFalse);
    }

    public boolean isPopupWindowAnimationOn() {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowAnimation}, false);
        if (el == null) {
            return DefPopupWindowAnimation;
        } else {
            return StrUtils.StrTrue.equals(el.getTextContent());
        }
    }

    public void setPopupWindowAnimationOn(boolean value) {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowAnimation}, true);
        el.setTextContent(value ? StrUtils.StrTrue : StrUtils.StrFalse);
    }

    public int getPopupWindowDelayTime() {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowDelayTime}, true);
        try {
            return new Integer(StringEscapeUtils.unescapeXml(el.getTextContent()));
        } catch (Exception ex) {
            return DefPopupWindowDelayTime;
        }
    }

    public void setPopupWindowDelayTime(int value) {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowDelayTime}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(Integer.toString(value)));
    }

    public int getPopupWindowWidth() {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowWidth}, true);
        try {
            return new Integer(StringEscapeUtils.unescapeXml(el.getTextContent()));
        } catch (Exception ex) {
            return DefPopupWindowWidth;
        }
    }

    public void setPopupWindowWidth(int value) {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowWidth}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(Integer.toString(value)));
    }

    public int getPopupWindowHeight() {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowHeight}, true);
        try {
            return new Integer(StringEscapeUtils.unescapeXml(el.getTextContent()));
        } catch (Exception ex) {
            return DefPopupWindowHeight;
        }
    }

    public void setPopupWindowHeight(int value) {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowHeight}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(Integer.toString(value)));
    }

    public int getPopupWindowPaddingWidth() {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowPaddingWidth}, true);
        try {
            return new Integer(StringEscapeUtils.unescapeXml(el.getTextContent()));
        } catch (Exception ex) {
            return DefPopupWindowPaddingWidth;
        }
    }

    public void setPopupWindowPaddingWidth(int value) {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowPaddingWidth}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(Integer.toString(value)));
    }

    public int getPopupWindowPosition() {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowPosition}, true);
        try {
            return new Integer(StringEscapeUtils.unescapeXml(el.getTextContent()));
        } catch (Exception ex) {
            return DefPopupWindowPosition;
        }
    }

    public void setPopupWindowPosition(int value) {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyPopupWindow, KeyPopupWindowPosition}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(Integer.toString(value)));
    }

    public int getMouseDblClickSensitivity() {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyMouseDblClickSensitivity}, true);
        try {
            return new Integer(StringEscapeUtils.unescapeXml(el.getTextContent()));
        } catch (Exception ex) {
            return DefMouseDblClickSensitivity;
        }
    }

    public void setMouseDblClickSensitivity(int value) {
        Element el = XmlParser.getElement(null, new String[]{KeyView, KeyMouseDblClickSensitivity}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(Integer.toString(value)));
    }

    /*----------------------------------------------------------------------------
     * Email Accounts
     */
    public List<ItemEmailAccount> getEmailAccounts() {
        List<ItemEmailAccount> result = new ArrayList<ItemEmailAccount>();

        Element eleEmailAccounts = XmlParser.getElement(null, new String[]{KeyEmailAccounts}, false);
        if (eleEmailAccounts != null) {
            NodeList childNodes = eleEmailAccounts.getChildNodes();
            for (int i = 0; i < childNodes.getLength(); i++) {
                Node childNode = childNodes.item(i);
                if (childNode.getNodeType() != Node.ELEMENT_NODE) {
                    continue;
                }

                ItemEmailAccount ea = new ItemEmailAccount();

                NodeList infoNodes = childNode.getChildNodes();
                for (int k = 0; k < infoNodes.getLength(); k++) {
                    Node node = infoNodes.item(k);

                    if (node.getNodeName().equals(KeyEmailAddress)) {
                        ea.setEmailAddress(Crypto.decryptStr_256(
                                StringEscapeUtils.unescapeXml(node.getTextContent().trim()), getRandomPassword()));
                    } else if (node.getNodeName().equals(KeyEmailPassword)) {
                        ea.setPassword(Crypto.decryptStr_256(
                                StringEscapeUtils.unescapeXml(node.getTextContent().trim()), getRandomPassword()));
                    }
                }//for k

                result.add(ea);
            }//for i
        }

        return result;
    }

    public void setEmailAccounts(List<ItemEmailAccount> emailAccounts) {
        Element eleEmailAccounts = XmlParser.getElement(null, new String[]{KeyEmailAccounts}, true);
        XmlParser.removeAllChildNodes(eleEmailAccounts);

        if (emailAccounts.isEmpty()) {
            return;
        }

        Document document = XmlParser.getDocument();
        for (ItemEmailAccount ea : emailAccounts) {
            Element item = document.createElement(KeyItem);
            eleEmailAccounts.appendChild(item);

            Element nodeInfo = document.createElement(KeyEmailAddress);
            item.appendChild(nodeInfo);
            nodeInfo.setTextContent(StringEscapeUtils.escapeXml(
                    Crypto.encryptStr_256(ea.getEmailAddress(), getRandomPassword())));

            nodeInfo = document.createElement(KeyEmailPassword);
            item.appendChild(nodeInfo);
            nodeInfo.setTextContent(StringEscapeUtils.escapeXml(
                    Crypto.encryptStr_256(ea.getPassword(), getRandomPassword())));
        }//for
    }

    /*----------------------------------------------------------------------------
     * Email Providers
     */
    public List<ItemEmailProvider> getEmailProviders() {
        List<ItemEmailProvider> result = new ArrayList<ItemEmailProvider>();

        Element eleEmailProviders = XmlParser.getElement(null, new String[]{KeyEmailProviders}, false);
        if (eleEmailProviders != null) {
            NodeList childNodes = eleEmailProviders.getChildNodes();
            for (int i = 0; i < childNodes.getLength(); i++) {
                Node childNode = childNodes.item(i);
                if (childNode.getNodeType() != Node.ELEMENT_NODE) {
                    continue;
                }

                ItemEmailProvider ep = new ItemEmailProvider();
                NamedNodeMap attributes = childNode.getAttributes();
                if (attributes != null) {
                    Node attrName = attributes.getNamedItem(AttrName);
                    if (attrName != null) {
                        ep.setName(StringEscapeUtils.unescapeXml(attrName.getNodeValue().trim()));
                    }
                }

                NodeList infoNodes = childNode.getChildNodes();
                for (int k = 0; k < infoNodes.getLength(); k++) {
                    Node node = infoNodes.item(k);

                    if (node.getNodeName().equals(KeyEmailPattern)) {
                        ep.setEmailPattern(StringEscapeUtils.unescapeXml(node.getTextContent().trim()));
                    } else if (node.getNodeName().equals(KeyImapHost)) {
                        ep.setImapHost(StringEscapeUtils.unescapeXml(node.getTextContent().trim()));
                    } else if (node.getNodeName().equals(KeyImapPort)) {
                        try {
                            ep.setImapPort(new Integer(node.getTextContent().trim()));
                        } catch (Exception ex) {
                            ep.setImapPort(DefImapPort);
                        }
                    }
                }//for k

                result.add(ep);
            }//for i
        }//if

        return result;
    }

    public void setEmailProviders(List<ItemEmailProvider> emailProviders) {
        Element eleEmailProviders = XmlParser.getElement(null, new String[]{KeyEmailProviders}, true);
        XmlParser.removeAllChildNodes(eleEmailProviders);

        if (emailProviders.isEmpty()) {
            return;
        }

        Document document = XmlParser.getDocument();
        for (ItemEmailProvider ep : emailProviders) {
            Element item = document.createElement(KeyItem);
            eleEmailProviders.appendChild(item);

            item.setAttribute(AttrName, StringEscapeUtils.escapeXml(ep.getName()));

            Element nodeInfo = document.createElement(KeyEmailPattern);
            item.appendChild(nodeInfo);
            nodeInfo.setTextContent(StringEscapeUtils.escapeXml(ep.getEmailPattern()));

            nodeInfo = document.createElement(KeyImapHost);
            item.appendChild(nodeInfo);
            nodeInfo.setTextContent(StringEscapeUtils.escapeXml(ep.getImapHost()));

            nodeInfo = document.createElement(KeyImapPort);
            item.appendChild(nodeInfo);
            nodeInfo.setTextContent(StringEscapeUtils.escapeXml(Integer.toString(ep.getImapPort())));
        }//for
    }

    /*----------------------------------------------------------------------------
     * Notification
     */
    public boolean isPlaySoundOn() {
        Element el = XmlParser.getElement(null, new String[]{KeyNotification, KeyPlaySound}, false);
        if (el == null) {
            return DefPlaySound;
        } else {
            return StrUtils.StrTrue.equals(el.getAttribute(AttrEnabled));
        }
    }

    public void setPlaySoundOn(boolean value) {
        Element el = XmlParser.getElement(null, new String[]{KeyNotification, KeyPlaySound}, true);
        el.setAttribute(AttrEnabled, value ? StrUtils.StrTrue : StrUtils.StrFalse);
    }

    public File getPlaySoundFile() {
        Element el = XmlParser.getElement(null, new String[]{KeyNotification, KeySoundFile}, false);
        if (el == null) {
            return new File("");
        } else {
            return new File(StringEscapeUtils.unescapeXml(el.getTextContent().trim()));
        }
    }

    public void setPlaySoundFile(File file) {
        Element el = XmlParser.getElement(null, new String[]{KeyNotification, KeySoundFile}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(file.getAbsolutePath()));
    }

    /*----------------------------------------------------------------------------
     * UserActions
     */
    public File getLastWorkingDir() {
        Element el = XmlParser.getElement(null, new String[]{KeyUserActions, KeyLastWorkingDir}, false);
        if (el == null) {
            return new File("");
        } else {
            return new File(StringEscapeUtils.unescapeXml(el.getTextContent().trim()));
        }
    }

    public void setLastWorkingDir(File file) {
        Element el = XmlParser.getElement(null, new String[]{KeyUserActions, KeyLastWorkingDir}, true);
        el.setTextContent(StringEscapeUtils.escapeXml(file.getAbsolutePath()));
    }

    /*----------------------------------------------------------------------------
     * AppPreferences
     */
    public boolean isAutoUpdateOn() {
        Element el = XmlParser.getElement(null, new String[]{KeyAppPreferences, KeyAutoUpdate}, false);
        if (el == null) {
            return DefAutoUpdate;
        } else {
            return StrUtils.StrTrue.equals(el.getAttribute(AttrEnabled));
        }
    }

    public void setAutoUpdateOn(boolean value) {
        Element el = XmlParser.getElement(null, new String[]{KeyAppPreferences, KeyAutoUpdate}, true);
        el.setAttribute(AttrEnabled, value ? StrUtils.StrTrue : StrUtils.StrFalse);
    }

    /*----------------------------------------------------------------------------
     * SINGLETON METHODS
     */
    public static Preferences getInstance() {
        return PreferencesHolder.INSTANCE;
    }

    private static class PreferencesHolder {

        private static final Preferences INSTANCE = new Preferences();
    }
}
